import { _decorator, assetManager, Color, Component, director, geometry, instantiate, Mesh, MeshRenderer, Node, sys, utils, Vec3 } from 'cc';
import builder from './RoomBuilder';
import HIT, { HitCtrl } from '../ctrl/HitCtrl';
import Net from '../frame/managers/NetMgr';
import configs from '../Configs';
const tempV3_1 = new Vec3();
const tempV3_2 = new Vec3();

const size = { end: { x: 0.5, y: -0.2 }, start: { x: 0.5, y: 0.88 }, pos: { x: -1, y: -0.9 } }


class DecoUtils {

     async createModel(base64){
        return await  Net.fetchGet(configs.serverUrl, { base64: base64 })
    }
    async checkModel(uuid){
        return await  Net.fetchGet(configs.serverUrl, { task: uuid })
    }
    async loadRemoteModel(glb: string, scale = 0.35, extraInfo: { start: any, end: any, pos: any } = size, rotate = 90): Promise<Node | null> {
        return new Promise(async (resolve, reject) => {
            try {
                if (!glb) {
                    resolve(null);
                    return;
                }
                assetManager.loadRemote(glb, (err, asset) => {
                    if (err) {
                        console.error("error", err);
                        reject(err);
                        return;
                    }

                    const model = instantiate(asset.nativeAsset.model) as Node;

                    this.addModelToScene(model, scale, extraInfo);

                    resolve(model);
                });

            } catch (error) {
                console.error("Failed to load remote model:", error);
                reject(error);
            }
        });
    }
    addModelToScene(model: Node, scale = 0.35, extraInfo: { start: any, end: any, pos: any } = size, rotate = 90) {
        console.log("model==", model);
        // 设置父节点，如果没有指定则使用当前场景
        const targetParent = director.getScene();
        model.parent = targetParent;
        // 获取网格渲染器
        const mesh = model.getComponent(MeshRenderer) || model.getComponentInChildren(MeshRenderer) as MeshRenderer;
        if (mesh) {
            mesh.node.layer = 1 << 0;

            mesh.model.worldBounds.getBoundary(tempV3_1, tempV3_2);
            const modelSize = Vec3.subtract(new Vec3(), tempV3_2, tempV3_1);
            const minY = tempV3_1.y;

            model.setScale(scale, scale, scale);

            if (extraInfo && extraInfo.start && extraInfo.end && extraInfo.pos) {
                // 计算墙的方向向量
                const wallDirection = new Vec3(
                    extraInfo.end.x - extraInfo.start.x,
                    0,
                    extraInfo.end.y - extraInfo.start.y
                );
                wallDirection.normalize();

                // 计算墙的中心点
                const wallCenter = new Vec3(
                    (extraInfo.start.x + extraInfo.end.x) / 2,
                    0,
                    (extraInfo.start.y + extraInfo.end.y) / 2
                );

                // 房间中心点
                const roomCenter = new Vec3(extraInfo.pos.x, 0, extraInfo.pos.y);

                // 直接使用墙的方向作为模型的朝向（沿着墙的方向）
                const angle = Math.atan2(wallDirection.x, wallDirection.z);
                model.setRotationFromEuler(0, angle * (180 / Math.PI) + rotate, 0);

                // 计算从房间中心到墙的方向（用于定位）
                const centerToWall = Vec3.subtract(new Vec3(), wallCenter, roomCenter);
                centerToWall.y = 0;
                centerToWall.normalize();

                // 计算模型在墙边的位置
                const modelDepth = Math.max(modelSize.x, modelSize.z) / 2;
                const wallNormal = Vec3.multiplyScalar(new Vec3(), centerToWall, -1);
                const offsetDistance = modelDepth + 0.1;
                const modelPosition = Vec3.add(new Vec3(), wallCenter,
                    Vec3.multiplyScalar(new Vec3(), wallNormal, offsetDistance)
                );

                modelPosition.y = -minY * scale;
                model.setPosition(modelPosition);

                console.log("Wall analysis:", {
                    wallDirection: wallDirection,
                    wallCenter: wallCenter,
                    roomCenter: roomCenter,
                    angle: angle * (180 / Math.PI),
                    testData: {
                        start: extraInfo.start,
                        end: extraInfo.end,
                        pos: extraInfo.pos
                    }
                });
            } else {
                // 如果没有extraInfo，使用默认设置
                const rooms = builder.dataManager.getAllRoomData();
                const length = Math.min(rooms.length, 1 + Math.floor(Math.random() * 3.1))
                const room = rooms[length]
                model.setRotationFromEuler(0, 180, 0);
                model.setPosition(room.position.x, -minY * scale, room.position.y);
            }
        }
        console.log("model loaded successfully", model, mesh);

        if (mesh) {

            // 添加到hitMap - 新增部分
            // this.addModelToHitMap(model, mesh);
            HIT.addNode(model, mesh)

        }



    }
    private addModelToHitMap(model: Node, mesh: MeshRenderer) {
        // 获取模型的本地边界框
        const modelComp = mesh.model;
        if (!modelComp) {
            console.warn("Mesh component not found");
            return;
        }
        // 添加到hitMap
        HIT.hitMap.set(model,modelComp);

        console.log("模型已添加到hitMap:", model.name);
    }

    // 计算点在向量上的投影距离
    getProjectionDistance(
        pointX: number,
        pointZ: number,
        lineStart: { x: number, y: number },
        dirNormalizedX: number,
        dirNormalizedZ: number
    ): number {
        // 计算点到直线起点的向量
        const vx = pointX - lineStart.x;
        const vz = pointZ - lineStart.y;

        // 计算投影长度 (点积)
        return vx * dirNormalizedX + vz * dirNormalizedZ;
    }
    /**
      * 计算切线
      * @param positions 顶点位置数组
      * @param normals 法线数组
      * @param uvs UV坐标数组
      * @param indices 索引数组
      * @returns 切线数组
      */
    public calculateTangents(positions: number[], normals: number[], uvs: number[], indices: number[]): number[] {
        let tangents: number[] = new Array(positions.length / 3 * 4).fill(0);
        let tan1: number[] = new Array(positions.length).fill(0);
        let tan2: number[] = new Array(positions.length).fill(0);

        // 为了减少依赖，我们可以直接使用数组来替代Vec3和Vec2
        const handleTriangle = (a: number, b: number, c: number) => {
            // 获取顶点位置
            const aX = positions[a * 3];
            const aY = positions[a * 3 + 1];
            const aZ = positions[a * 3 + 2];

            const bX = positions[b * 3];
            const bY = positions[b * 3 + 1];
            const bZ = positions[b * 3 + 2];

            const cX = positions[c * 3];
            const cY = positions[c * 3 + 1];
            const cZ = positions[c * 3 + 2];

            // 获取UV坐标
            const uvAX = uvs[a * 2];
            const uvAY = uvs[a * 2 + 1];

            const uvBX = uvs[b * 2];
            const uvBY = uvs[b * 2 + 1];

            const uvCX = uvs[c * 2];
            const uvCY = uvs[c * 2 + 1];

            // 计算位置差值
            const vBX = bX - aX;
            const vBY = bY - aY;
            const vBZ = bZ - aZ;

            const vCX = cX - aX;
            const vCY = cY - aY;
            const vCZ = cZ - aZ;

            // 计算UV差值
            const uvBX_diff = uvBX - uvAX;
            const uvBY_diff = uvBY - uvAY;

            const uvCX_diff = uvCX - uvAX;
            const uvCY_diff = uvCY - uvAY;

            // 计算r
            const r = 1.0 / (uvBX_diff * uvCY_diff - uvCX_diff * uvBY_diff);

            // 计算sdir和tdir
            const sdirX = (uvCY_diff * vBX - uvBY_diff * vCX) * r;
            const sdirY = (uvCY_diff * vBY - uvBY_diff * vCY) * r;
            const sdirZ = (uvCY_diff * vBZ - uvBY_diff * vCZ) * r;

            const tdirX = (uvBX_diff * vCX - uvCX_diff * vBX) * r;
            const tdirY = (uvBX_diff * vCY - uvCX_diff * vBY) * r;
            const tdirZ = (uvBX_diff * vCZ - uvCX_diff * vBZ) * r;

            // 累加到tan1和tan2
            tan1[a * 3] += sdirX;
            tan1[a * 3 + 1] += sdirY;
            tan1[a * 3 + 2] += sdirZ;

            tan1[b * 3] += sdirX;
            tan1[b * 3 + 1] += sdirY;
            tan1[b * 3 + 2] += sdirZ;

            tan1[c * 3] += sdirX;
            tan1[c * 3 + 1] += sdirY;
            tan1[c * 3 + 2] += sdirZ;

            tan2[a * 3] += tdirX;
            tan2[a * 3 + 1] += tdirY;
            tan2[a * 3 + 2] += tdirZ;

            tan2[b * 3] += tdirX;
            tan2[b * 3 + 1] += tdirY;
            tan2[b * 3 + 2] += tdirZ;

            tan2[c * 3] += tdirX;
            tan2[c * 3 + 1] += tdirY;
            tan2[c * 3 + 2] += tdirZ;
        };

        // 处理每个三角形
        for (let i = 0; i < indices.length; i += 3) {
            const a = indices[i];
            const b = indices[i + 1];
            const c = indices[i + 2];
            handleTriangle(a, b, c);
        }

        // 计算最终的切线
        const pointSize = positions.length / 3;
        for (let i = 0; i < pointSize; i++) {
            // 获取法线
            const nX = normals[i * 3];
            const nY = normals[i * 3 + 1];
            const nZ = normals[i * 3 + 2];

            // 获取tan1
            const tX = tan1[i * 3];
            const tY = tan1[i * 3 + 1];
            const tZ = tan1[i * 3 + 2];

            // Gram-Schmidt 正交化
            // 计算点积
            const dotProduct = nX * tX + nY * tY + nZ * tZ;

            // 计算投影
            const projX = nX * dotProduct;
            const projY = nY * dotProduct;
            const projZ = nZ * dotProduct;

            // 切线 = t - 投影
            let tangentX = tX - projX;
            let tangentY = tY - projY;
            let tangentZ = tZ - projZ;

            // 归一化切线
            const length = Math.sqrt(tangentX * tangentX + tangentY * tangentY + tangentZ * tangentZ);
            if (length > 0) {
                tangentX /= length;
                tangentY /= length;
                tangentZ /= length;
            }

            // 存储切线
            tangents[i * 4] = tangentX;
            tangents[i * 4 + 1] = tangentY;
            tangents[i * 4 + 2] = tangentZ;

            // 计算手性
            // 计算叉乘 n × t
            const crossX = nY * tZ - nZ * tY;
            const crossY = nZ * tX - nX * tZ;
            const crossZ = nX * tY - nY * tX;

            // 与tan2点积
            const handedness = (crossX * tan2[i * 3] + crossY * tan2[i * 3 + 1] + crossZ * tan2[i * 3 + 2]) < 0.0 ? -1.0 : 1.0;
            tangents[i * 4 + 3] = handedness;
        }

        return tangents;
    }



    createMesh(wallNode: Node, startPoint: { x: number, y: number }, endPoint: { x: number, y: number }, thickness: number, height: number): Mesh {
        // 计算墙体中点
        const midX = (startPoint.x + endPoint.x) / 2;
        const midY = height / 2; // 墙体中心点y坐标
        const midZ = (startPoint.y + endPoint.y) / 2;
        // 设置墙体位置
        wallNode.position = new Vec3(midX, 0, midZ);
        // 计算墙体方向向量
        const dirX = endPoint.x - startPoint.x;
        const dirZ = endPoint.y - startPoint.y;
        const dirLength = Math.sqrt(dirX * dirX + dirZ * dirZ);
        const dirNormalizedX = dirX / dirLength;
        const dirNormalizedZ = dirZ / dirLength;

        // 计算墙体长度
        const length = dirLength;
        // 创建墙体网格
        // // 计算法向量（垂直于墙体方向）
        const normalX = -dirNormalizedZ;
        const normalZ = dirNormalizedX;

        const p1x = - (dirNormalizedX * length / 2) - (normalX * thickness / 2);
        const p1z = - (dirNormalizedZ * length / 2) - (normalZ * thickness / 2);

        const p2x = + (dirNormalizedX * length / 2) - (normalX * thickness / 2);
        const p2z = + (dirNormalizedZ * length / 2) - (normalZ * thickness / 2);

        const p3x = + (dirNormalizedX * length / 2) + (normalX * thickness / 2);
        const p3z = + (dirNormalizedZ * length / 2) + (normalZ * thickness / 2);

        const p4x = - (dirNormalizedX * length / 2) + (normalX * thickness / 2);
        const p4z = - (dirNormalizedZ * length / 2) + (normalZ * thickness / 2);

        // 创建顶点坐标 - 采用24个顶点方式（每个面4个顶点）
        const positions: number[] = [

            // 顶面 (y = height)
            p1x, height, p1z,  // 4
            p2x, height, p2z,  // 5
            p3x, height, p3z,  // 6
            p4x, height, p4z,  // 7

            // 侧面1 (p1-p2边)
            p1x, 0, p1z,       // 8
            p2x, 0, p2z,       // 9
            p2x, height, p2z,  // 10
            p1x, height, p1z,  // 11

            // // 侧面2 (p2-p3边)
            p2x, 0, p2z,       // 12
            p3x, 0, p3z,       // 13
            p3x, height, p3z,  // 14
            p2x, height, p2z,  // 15

            // 侧面3 (p3-p4边)
            p3x, 0, p3z,       // 16
            p4x, 0, p4z,       // 17
            p4x, height, p4z,  // 18
            p3x, height, p3z,  // 19

            // 侧面4 (p4-p1边)
            p4x, 0, p4z,       // 20
            p1x, 0, p1z,       // 21
            p1x, height, p1z,  // 22
            p4x, height, p4z   // 23
        ];

        // 索引 - 为侧面反转三角形顺序
        const indices: number[] = [
            // 顶面 (顺时针，从顶部看)
            0, 3, 2, 0, 2, 1,

            // 侧面1 (前面) - 反转顺序
            4, 7, 6, 4, 6, 5,

            // 侧面2 (右面) - 反转顺序
            8, 11, 10, 8, 10, 9,

            // 侧面3 (后面) - 反转顺序
            12, 15, 14, 12, 14, 13,

            // 侧面4 (左面) - 反转顺序
            16, 19, 18, 16, 18, 17
        ];

        // 正确计算每个面的法线
        const normals: number[] = [
            // 顶面法线 (向上)
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,

            // 侧面1法线 (p1-p2边) - 使用计算的法线而不是固定值
            -normalX, 0, -normalZ,
            -normalX, 0, -normalZ,
            -normalX, 0, -normalZ,
            -normalX, 0, -normalZ,

            // 侧面2法线 (p2-p3边)
            dirNormalizedX, 0, dirNormalizedZ,
            dirNormalizedX, 0, dirNormalizedZ,
            dirNormalizedX, 0, dirNormalizedZ,
            dirNormalizedX, 0, dirNormalizedZ,

            // 侧面3法线 (p3-p4边)
            normalX, 0, normalZ,
            normalX, 0, normalZ,
            normalX, 0, normalZ,
            normalX, 0, normalZ,

            // 侧面4法线 (p4-p1边)
            -dirNormalizedX, 0, -dirNormalizedZ,
            -dirNormalizedX, 0, -dirNormalizedZ,
            -dirNormalizedX, 0, -dirNormalizedZ,
            -dirNormalizedX, 0, -dirNormalizedZ
        ];


        // 计算比例UV坐标 (距离/10)
        // 使用实际尺寸计算UV
        const heightScale = height / 10;       // 高度缩放
        const lengthScale = dirLength / 10;    // 长度缩放
        const thicknessScale = thickness / 10; // 厚度缩放

        // 优化的UV坐标
        const uvs: number[] = [
            // 顶面UV (基于长度和厚度)
            0, 0,                   // 左下
            lengthScale, 0,         // 右下
            lengthScale, thicknessScale, // 右上
            0, thicknessScale,      // 左上

            // 侧面1UV (基于长度和高度) - p1-p2边
            0, 0,                 // 左下
            lengthScale, 0,       // 右下
            lengthScale, heightScale, // 右上
            0, heightScale,       // 左上

            // 侧面2UV (基于厚度和高度) - p2-p3边
            0, 0,                  // 左下
            thicknessScale, 0,     // 右下
            thicknessScale, heightScale, // 右上
            0, heightScale,        // 左上

            // 侧面3UV (基于长度和高度) - p3-p4边
            0, 0,                 // 左下
            lengthScale, 0,       // 右下
            lengthScale, heightScale, // 右上
            0, heightScale,       // 左上

            // 侧面4UV (基于厚度和高度) - p4-p1边
            0, 0,                  // 左下
            thicknessScale, 0,     // 右下
            thicknessScale, heightScale, // 右上
            0, heightScale         // 左上
        ];


        // 创建Mesh
        const boundingBox = this.calculateBoundingBox(positions);
        const mesh = utils.MeshUtils.createMesh({
            positions: positions,
            indices: indices,
            normals: normals,
            uvs: uvs,
            minPos: boundingBox.minPos,
            maxPos: boundingBox.maxPos
        });

        return mesh;

    }
    // 计算顶点数组的边界框
    calculateBoundingBox(positions) {
        const xs = positions.filter((_, i) => i % 3 === 0);
        const ys = positions.filter((_, i) => i % 3 === 1);
        const zs = positions.filter((_, i) => i % 3 === 2);

        return {
            minPos: {
                x: Math.min(...xs),
                y: Math.min(...ys),
                z: Math.min(...zs)
            },
            maxPos: {
                x: Math.max(...xs),
                y: Math.max(...ys),
                z: Math.max(...zs)
            }
        };
    }


    /**
    * 保存识别结果为JSON文件
    * @param data 要保存的数据对象
    * @param filename 可选的文件名(不包含.json后缀)
    */
    public saveRecognitionDataAsJson(data: any, filename?: string): void {
        try {
            // 检查数据是否有效
            if (!data) {
                throw new Error('数据无效');
            }

            // 转换为JSON字符串(使用格式化以便阅读)
            const jsonString = JSON.stringify(data, null, 2);

            // 创建Blob对象
            const blob = new Blob([jsonString], { type: 'application/json' });

            // 创建下载链接
            const url = URL.createObjectURL(blob);

            // 创建一个临时的a标签用于下载
            const downloadLink = document.createElement('a');
            downloadLink.href = url;

            // 设置文件名(默认使用时间戳)
            const defaultFilename = `recognition-result-${new Date().getTime()}`;
            downloadLink.download = `${filename || defaultFilename}.json`;

            // 添加到DOM, 触发点击后移除
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);

            // 释放URL对象
            URL.revokeObjectURL(url);

            console.log('数据已成功保存为JSON文件');
        } catch (error) {
            console.error('保存数据为JSON文件时出错:', error);
            throw error;
        }
    }

    /**
     * 上传并解析JSON文件
     */
    uploadAndParseJsonFile(): Promise<string> {
        return new Promise((resolve, reject) => {
            // 创建文件输入框
            const fileInput = document.createElement('input');
            fileInput.type = 'file';
            fileInput.accept = '.json,application/json';
            fileInput.style.display = 'none';
            document.body.appendChild(fileInput);

            // 监听文件选择事件
            fileInput.addEventListener('change', (event) => {
                const target = event.target as HTMLInputElement;
                const files = target.files;

                if (!files || files.length === 0) {
                    document.body.removeChild(fileInput);
                    reject(new Error('没有选择文件'));
                    return;
                }

                const file = files[0];
                console.log('选择的JSON文件:', file.name);

                // 读取文件内容
                const reader = new FileReader();

                reader.onload = (e) => {
                    try {
                        // 解析JSON
                        const jsonContent = JSON.parse(e.target.result as string);

                        // 提取roomContent字段
                        let roomContent;
                        if (jsonContent.roomContent) {
                            // 如果roomContent是字符串，尝试解析它
                            if (typeof jsonContent.roomContent === 'string') {
                                try {
                                    roomContent = jsonContent.roomContent;
                                } catch (parseError) {
                                    console.warn('roomContent不是有效的JSON字符串');
                                    roomContent = jsonContent.roomContent;
                                }
                            } else {
                                roomContent = jsonContent.roomContent;
                            }
                        } else {
                            // 如果不存在roomContent字段，使用整个JSON
                            roomContent = jsonContent;
                        }

                        // 移除文件输入框
                        document.body.removeChild(fileInput);

                        resolve(roomContent);
                    } catch (parseError) {
                        document.body.removeChild(fileInput);
                        reject(new Error(`解析JSON文件失败: ${parseError.message}`));
                    }
                };

                reader.onerror = () => {
                    document.body.removeChild(fileInput);
                    reject(new Error('读取文件时发生错误'));
                };

                // 开始读取文件
                reader.readAsText(file);
            });

            // 触发文件选择对话框
            fileInput.click();
        });
    }

    /*
  * 创建DOM上传元素并获取选择的图片文件的base64
  * @param callback 上传完成后的回调，带有base64字符串参数
  */
    public uploadImageAndGetBase64(callback: (base64: string) => void): void {
        // 检查是否在Web平台
        if (!sys.isBrowser) {
            console.error('此方法只能在Web平台使用');
            return;
        }

        // 创建input元素
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = 'image/jpeg,image/png';
        input.style.position = 'absolute';
        input.style.top = '-100px'; // 隐藏元素

        // 添加到DOM
        document.body.appendChild(input);

        // 监听change事件
        input.onchange = () => {
            if (input.files && input.files.length > 0) {
                const file = input.files[0];

                // 检查文件类型
                if (!/^image\/(jpeg|png)$/i.test(file.type)) {
                    console.error('请选择JPG或PNG格式的图片');
                    document.body.removeChild(input);
                    callback('');
                    return;
                }

                // 创建FileReader读取文件
                const reader = new FileReader();
                reader.onload = (e) => {
                    const base64 = e.target?.result as string;
                    callback(base64);

                    // 清理DOM
                    document.body.removeChild(input);
                };
                reader.onerror = () => {
                    console.error('读取文件失败');
                    document.body.removeChild(input);
                    callback('');
                };

                // 读取文件为DataURL (base64)
                reader.readAsDataURL(file);
            } else {
                // 用户取消上传
                document.body.removeChild(input);
                callback('');
            }
        };
        // 触发点击事件打开文件选择框

        input.click();


    }

}
const DecoUtil = new DecoUtils();

export default DecoUtil;

